package com.jackstraw.jack_base.datasource.moredatasource.seata.aop;


import com.jackstraw.jack_base.datasource.database.parten.entity.bean.DataBaseRoutingDataSource;
import com.jackstraw.jack_base.datasource.database.parten.entity.bean.DataBaseTemplate;
import com.jackstraw.jack_base.util.StringUtil;
import com.jackstraw.jack_base.util.AspectUtil;
import io.seata.rm.datasource.DataSourceProxy;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 数据源切面类
 */
@Slf4j
@Aspect
@Component
public class DataBaseSeataCutAspect {

    /**
     * 构建数据源是中间类切面
     */
    @Pointcut(" execution(* com.jackstraw.jack_base.datasource.database.parten.entity.bean.DataBaseTemplateCut.cut(..))")
    public void dataSourceCut() {

    }

    @Around("dataSourceCut()")
    public Object dataSourceCutByAround(ProceedingJoinPoint joinPoint) {
        long currentTimeMillis = System.currentTimeMillis();
        //获取代理对象和方法
        Map<String, Object> map = AspectUtil.getClassAndMethod(joinPoint);
        //获取代理的对象
        Class aClass = (Class) map.get("aClass");
        String className = aClass.getName();
        //获取Method
        Method method = (Method) map.get("method");
        String methodName = method.getName();// 获得目标方法名
        if (StringUtil.StringUtilsBylang3.isEmpty(className)
                || StringUtil.StringUtilsBylang3.isEmpty(methodName)) {
            log.error("切面异常，无法获取切面类名称或切面方法名称");
            throw new IllegalStateException("切面异常，无法获取切面类名称或切面方法名称");
        }
        log.info("AOP环绕通知【时间戳为:{}】=========={}#{}方法执行开始", currentTimeMillis, className, methodName);
        Object result = null;
        try {
            //获取参数
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length == 0) {
                result = joinPoint.proceed();
            } else {
                result = joinPoint.proceed(args);
            }
        } catch (Throwable e) {
            log.error("AOP环绕通知【时间戳为:{}】=========={}#{}方法执行异常。异常原因为：{}=============>", currentTimeMillis, className, methodName, e);
            throw new IllegalStateException("AOP环绕通知【时间戳为:" + currentTimeMillis + "】==========" + className + "#" + methodName + "方法执行异常。异常原因为：{}=============>");
        } finally {
            log.info("AOP环绕通知【时间戳为:{}】=========={}#{}方法执行结束", currentTimeMillis, className, methodName);
        }
        if (!(result instanceof DataBaseTemplate)) {
            throw new IllegalStateException("dataBase切面返回结果异常");
        }

        DataBaseTemplate dataBaseTemplate = (DataBaseTemplate) result;

        DataBaseRoutingDataSource dataBaseRoutingDataSource = dataBaseTemplate.getDataBaseRoutingDataSource();

        Map<Object, DataSource> resolvedDataSources = dataBaseRoutingDataSource.getResolvedDataSources();
        DataSource resolvedDefaultDataSource = dataBaseRoutingDataSource.getResolvedDefaultDataSource();

        Map<Object, Object> targetDataSources = new HashMap<>();

        AtomicReference<String> defaultDataSourceName = new AtomicReference<>("");

        resolvedDataSources.keySet().stream().forEach(dataSourceName -> {
            DataSource dataSource = resolvedDataSources.get(dataSourceName);
            if (dataSource.equals(resolvedDefaultDataSource)) {
                defaultDataSourceName.set((String) dataSourceName);
            }
            targetDataSources.put(dataSourceName, new DataSourceProxy(dataSource));
        });

        /**
         * 做seata代理
         */
        dataBaseRoutingDataSource.setDefaultTargetDataSource(targetDataSources.get(defaultDataSourceName.get()));
        dataBaseRoutingDataSource.setTargetDataSources(targetDataSources);


        return result;
    }


}

